﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using CLUBsInterpreter.ObjectTypes;

namespace CLUBsInterpreter.AST
{
	/**
	 * <summary>
	 * Describes an abstract expression node in the AST.
	 * </summary>
	 **/
	abstract class ExpressionNode : AbstractSyntaxTree
	{
		/**
		 * <summary>
		 * The line number in the source code of this expression.
		 * </summary>
		 **/
		public uint LineNumber;
		/**
		 * <summary>
		 * The inferred type of this expression, set by the contextual analyzer (visitor).
		 * </summary>
		 **/
		public IdentifierType InferredType = IdentifierType.Unset;

		/**
		 * <summary>
		 * GetValue must be implemented to return a CLUBsObject resulting from the evaluation of the expression at runtime.
		 * </summary>
		 **/
		public abstract CLUBsObject GetValueReference();
	}

	/**
	 * <summary>
	 * Describes a binary expression node.
	 * </summary>
	 **/
	class BinaryExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The left-hand side of the binary expression.
		 * </summary>
		 **/
		public ExpressionNode Exp1;
		/**
		 * <summary>
		 * The operator applied in the binary expression.
		 * </summary>
		 **/
		public Operator Op;
		/**
		 * <summary>
		 * The right-hand side of the binary expression.
		 * </summary>
		 **/
		public ExpressionNode Exp2;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitBinaryExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitBinaryExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the binary expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "BinaryExpNode (op"+Op+")");
			// Print sub-expressions
			Exp1.PrintTree(outStream, depth + 1);
			Exp2.PrintTree(outStream, depth + 1);
		}

		public override CLUBsObject GetValueReference()
		{
			// Get sub-expression values, apply appropriate operator, and return result
			switch(Op)
			{
				case Operator.Plus:
					return (CLUBsInteger)Exp1.GetValueReference() + (CLUBsInteger)Exp2.GetValueReference();
				case Operator.Minus:
					return (CLUBsInteger)Exp1.GetValueReference() - (CLUBsInteger)Exp2.GetValueReference();
 				case Operator.Mult:
					return (CLUBsInteger)Exp1.GetValueReference() * (CLUBsInteger)Exp2.GetValueReference();
				case Operator.Div:
					return (CLUBsInteger)Exp1.GetValueReference() / (CLUBsInteger)Exp2.GetValueReference();
				case Operator.Is:
					/*if (Exp1.InferredType == IdentifierType.Null || Exp2.InferredType == IdentifierType.Null)
						return (CLUBsBoolean)(Exp1.InferredType == IdentifierType.Null && Exp2.InferredType == IdentifierType.Null);*/
					//Console.WriteLine("Comparing " + Exp1.GetValueReference() + " to " + Exp2.GetValueReference());
					return (CLUBsBoolean)(Exp1.GetValueReference() == Exp2.GetValueReference());
				default:
					return null;
			}


			 
		}
	}

	/**
	 * <summary>
	 * Describes a unary expression node.
	 * </summary>
	 **/
	class UnaryExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The operator applied in the unary expression.
		 * </summary>
		 **/
		public Operator Op;
		/**
		 * <summary>
		 * The expression to which the unary operator is applied.
		 * </summary>
		 **/
		public ExpressionNode Exp;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitUnaryExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitUnaryExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the unary expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "UnaryExpNode (op"+Op+")");
			// Print sub-expression
			Exp.PrintTree(outStream, depth + 1);
		}
        //Returns the not value, if that is the case 
		public override CLUBsObject GetValueReference()
		{
            switch (Op)
            { 
                case Operator.Not:
                    return !(CLUBsBoolean)Exp.GetValueReference();
                case Operator.Minus:
                    return (CLUBsInteger)Exp.GetValueReference() * (-1);
                default:
                    return null;
            }
		}
	}

	/**
	 * <summary>
	 * Describes a call expression node.
	 * </summary>
	 **/
	class CallExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The call statement node used in the expression.
		 * </summary>
		 **/
		public CallStmtNode Call;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitCallExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitCallExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print the call expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "CallExpNode");
			// Print the call statement subtree
			Call.PrintTree(outStream, depth + 1);
		}
        //Todo: check is right
		public override CLUBsObject GetValueReference()
		{
            CLUBsObject[] objects = new CLUBsObject[Call.Parameters.Count];
            int i = 0;
            foreach (ExpressionNode expression in Call.Parameters)
            {
                objects[i] = expression.GetValueReference();
                i++;
            }

            return StandardEnvironment.Instance.CallAction(Call.ActionObject, objects);              
		}
	}

	/**
	 * <summary>
	 * Describes a literal string expression node.
	 * </summary>
	 **/
	class StringExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The string object of this literal string expression.
		 * </summary>
		 **/
		public CLUBsString Value;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitStringExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitStringExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print string expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "StringExpNode");
		}

		public override CLUBsObject GetValueReference()
		{
			// Just return the CLUBsString object
			return Value;
		}
	}

	/**
	 * <summary>
	 * Describes an integer literal node.
	 * </summary>
	 **/
	class IntegerExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The actual value of this integer literal.
		 * </summary>
		 **/
		public CLUBsInteger Value;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitIntegerExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitIntegerExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print integer expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "IntegerExpNode");
		}

		public override CLUBsObject GetValueReference()
		{
			// Just return the CLUBsInteger
			return Value;
		}
	}

	/**
	 * <summary>
	 * Describes a variable used in an expression.
	 * </summary>
	 **/
	class VNameExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The identifier of the variable.
		 * </summary>
		 **/
		public Identifier Ident;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitVNameExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitVNameExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print VName expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "VNameExpNode");
			// Print the identifier subtree
			Ident.PrintTree(outStream, depth + 1);
		}
        //Todo: check if right muligvis done
		public override CLUBsObject GetValueReference()
		{
            return Ident.Reference;
        }
	}

	/**
	 * <summary>
	 * Describes a parenthesis expression node.
	 * </summary>
	 **/
	class ParenthesisExpNode : ExpressionNode
	{
		/**
		 * <summary>
		 * The expression contained within the parentheses.
		 * </summary>
		 **/
		public ExpressionNode Exp;

		/**
		 * <see cref="AbstractSyntaxTree.Visit"/>
		 **/
		public override void Visit(Visitor v)
		{
			// Call the appropriate Visitor method
			if (m_visitedOnce)
				v.VisitParenthesisExpNodeSecond(this);
			else
			{
				m_visitedOnce = true;
				v.VisitParenthesisExpNode(this);
			}
		}

		public override void PrintTree(TextWriter outStream, int depth)
		{
			// Print parenthesis expression node
			outStream.WriteLine(AbstractSyntaxTree.TabDepth(depth) + "ParenthesisExpNode");
			// Print the sub-expression tree
			Exp.PrintTree(outStream, depth + 1);
		}

		public override CLUBsObject GetValueReference()
		{
            return (CLUBsObject)Exp.GetValueReference();
		}
	}
}
